﻿#include "WriteWindow.h"
#include <windows.h>
#include <QDebug>
#include <QDateTime>

WriteWindow::WriteWindow(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);

    ui.start->setEnabled(true);
    ui.stop->setEnabled(false);
    ui.isEncode->setChecked(false);
    ui.isEncode->setEnabled(true);

    // 初始化
    _videoProgram = new VideoProgram();
    Matrix& matrix = _videoProgram->contextForWindId((void*)ui.content->winId())->glesContext()->matrix();

    matrix.setType(1);
    matrix.setRotate(180);
    matrix.setMove(0, 0, -1000);

    _inputDevice = new InputDevice();

    VideoCallBack videoFunction = std::bind(&WriteWindow::videoBuffer, this);
    _inputDevice->setVideoCallBack(videoFunction);

    AudioCallBack audioFunction = std::bind(
        &WriteWindow::audioBuffer, this,
        std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
        std::placeholders::_4, std::placeholders::_5);
    _inputDevice->setAudioCallBack(audioFunction);

    // 音频预览缓冲区
    _pcmBuffer.resetBuffer(BUFFERNOTIFYSIZE * 10);

    // 声音预览回调
    SoundCallBack soundCallBack = std::bind(
        &WriteWindow::playAudio, this,
        std::placeholders::_1, std::placeholders::_2);
    _outputDevice.setSoundCallBack(soundCallBack);

    // 绑定事件
    qRegisterMetaType<ImageType>("ImageType");
    connect(this, &WriteWindow::sendBuffer, this, &WriteWindow::onBuffer);

    // 枚举摄像头
    enumVideoDevices();

    // 枚举麦克风
    enumAudioDevices();
}

void WriteWindow::closeEvent(QCloseEvent* e)
{
    on_stop_clicked();

    if (_videoProgram)
    {
        delete _videoProgram;
        _videoProgram = nullptr;
    }

    if (_inputDevice)
    {
        delete _inputDevice;
        _inputDevice = nullptr;
    }

    this->deleteLater();
}

// 开始预览
void WriteWindow::on_start_clicked()
{
    ui.start->setEnabled(false);
    ui.stop->setEnabled(true);

    // mp4编码器
    if (ui.isEncode->isChecked())
    {
        h264_compress_begin(&_h264Ecoder, _selectVideoInfo.minCX, _selectVideoInfo.minCY);

        DShow::AudioInfo& audioInfoItem = _selectAudioDevice.caps.front();
        _accEncoder.open(audioInfoItem.maxSampleRate, audioInfoItem.maxChannels);

        _mp4Coder._filePathName = "a.mp4";
        _mp4Coder._videoWidth = _selectVideoInfo.minCX;
        _mp4Coder._videoHeight = _selectVideoInfo.minCY;
        _mp4Coder.initMp4Encoder(
            _accEncoder.audioDecodeInfo(),
            _accEncoder.inputSamples() / audioInfoItem.maxChannels,
            audioInfoItem.maxSampleRate);
    }
    else
    {
        // 声音播放器
        DShow::AudioInfo& audioInfoItem = _selectAudioDevice.caps.front();
        _outputDevice.open((void*)this->winId(), audioInfoItem.maxChannels, audioInfoItem.maxSampleRate);
        _outputDevice.start();
    }
    ui.isEncode->setEnabled(false);

    // 开始采集
    _inputDevice->setup(&_selectVideoDevice, &_selectVideoInfo, &_selectAudioDevice);
    _inputDevice->start();

    // 禁止控件自动刷新
    ui.content->setUpdatesEnabled(false);
}

// 停止预览
void WriteWindow::on_stop_clicked()
{
    ui.start->setEnabled(true);
    ui.stop->setEnabled(false);

    if (_inputDevice)
    {
        _inputDevice->stop();
    }

    // 编码器
    if (ui.isEncode->isChecked())
    {
        // 释放编码器
        _mp4Coder.closeMp4Encoder();
        h264_compress_end(&_h264Ecoder);
        _accEncoder.close();
    }
    else
    {
        // 声音播放器
        _outputDevice.stop();
    }
    ui.isEncode->setEnabled(true);

    // 开启控件自动刷新
    ui.content->setUpdatesEnabled(true);
    ui.content->update();
}

// 数据在主线程回调给渲染器(防止窗口动态变化崩溃)
void WriteWindow::onBuffer()
{
    if (!_videoProgram)
    {
        return;
    }

    std::shared_ptr<Device::VideoBuffer>& videoBuffer = _inputDevice->buffer();

    if (!videoBuffer)
    {
        return;
    }

    VideoBuffer* bufferPointer = new VideoBuffer();

    bufferPointer->width = videoBuffer->width;
    bufferPointer->height = videoBuffer->height;
    bufferPointer->buffer = videoBuffer->buffer;
    bufferPointer->type = (ImageType)videoBuffer->type;

    _videoProgram->draw(std::shared_ptr<VideoBuffer>(bufferPointer), (HWND)ui.content->winId());
}

// 摄像头选择
void WriteWindow::on_videoDevice_currentIndexChanged(int index)
{
    _selectVideoDevice = _videoDevices[index];

    ui.videoInfo->clear();
    for (DShow::VideoInfo& videoInfoItem : _selectVideoDevice.caps)
    {
        ui.videoInfo->addItem(QString::asprintf("%d x %d : %d",
            videoInfoItem.minCX, videoInfoItem.minCY, videoInfoItem.format));
    }
}

// 分辨率选择
void WriteWindow::on_videoInfo_currentIndexChanged(int index)
{
    if (index < 0 || index >= _selectVideoDevice.caps.size())
    {
        return;
    }

    _selectVideoInfo = _selectVideoDevice.caps[index];
}

void WriteWindow::on_audioDevice_currentIndexChanged(int index)
{
    _selectAudioDevice = _audioDevices[index];
}

// 摄像头数据回调
void WriteWindow::videoBuffer()
{
    emit sendBuffer();

    if (!_videoProgram)
    {
        return;
    }

    std::shared_ptr<Device::VideoBuffer>& videoBuffer = _inputDevice->buffer();

    if (!ui.isEncode->isChecked() || !videoBuffer)
    {
        return;
    }

    // 当h264编码器关闭时编码器宽高会置0，因此线程安全
    if (videoBuffer->width == _h264Ecoder.param->i_width &&
        videoBuffer->height == _h264Ecoder.param->i_height)
    {
        // 编码一帧数据
        h264_encode_frame(&_h264Ecoder, videoBuffer->buffer.get());

        // 计算两帧之间的间隔时间(以100纳秒为单位)
        QDateTime time = QDateTime::currentDateTime();
        int timeInt = time.toMSecsSinceEpoch();

        long long tempTakeTime = 0;

        if (startTime > 0)
        {
            tempTakeTime = timeInt - startTime;
        }
        startTime = timeInt;

        // 仅此帧第一次写入的片段需要加上与前一帧的时间间隔，本帧后面的片段是不需要间隔的
        _mp4Coder.mp4VEncode(_h264Ecoder.nal[0].p_payload, _h264Ecoder.nal[0].i_payload, tempTakeTime * 10000);

        // 写入，注意这里虽然循环多次写入，但只是写入一帧
        for (int i = 1; i < _h264Ecoder.nal_count; i++)
        {
            _mp4Coder.mp4VEncode(_h264Ecoder.nal[i].p_payload, _h264Ecoder.nal[i].i_payload, 0);
        }
    }
}

// 麦克风数据回调
void WriteWindow::audioBuffer(const DShow::AudioConfig& config, unsigned char* data,
    size_t size, long long startTime, long long stopTime)
{
    if (!ui.isEncode->isChecked())
    {
        _pcmBuffer.pushData((char*)data, size);
        return;
    }

    int ret = _accEncoder.encodeAudioData(data, size);

    // faac一般需要3、4个样本缓存才能产生一个数据包
    if (ret <= 0)
    {
        return;
    }

    _mp4Coder.mp4AEncode(_accEncoder.faacEncodeOutputBuffer(), ret);
}

// 声音播放器回调
void WriteWindow::playAudio(void* buffer, unsigned long size)
{
    if (_pcmBuffer.dataLength() >= size)
    {
        _pcmBuffer.data((char*)buffer, size);
    }
}

void WriteWindow::enumVideoDevices()
{
    // 枚举摄像头
    InputDevice::EnumVideoDevices(_videoDevices);

    ui.videoDevice->clear();
    for (DShow::VideoDevice& videoDeviceItem : _videoDevices)
    {
        ui.videoDevice->addItem(QString::fromStdWString(videoDeviceItem.name));
    }
}

void WriteWindow::enumAudioDevices()
{
    // 枚举麦克风
    InputDevice::EnumAudioDevices(_audioDevices);

    ui.audioDevice->clear();
    for (DShow::AudioDevice& audioDeviceItem : _audioDevices)
    {
        ui.audioDevice->addItem(QString::fromStdWString(audioDeviceItem.name));
    }
}
